﻿///////////////////////////////////////////////////////////////////////////////
//	Copyright 2013 JASDev International
//
//	Licensed under the Apache License, Version 2.0 (the "License");
//	you may not use this file except in compliance with the License.
//	You may obtain a copy of the License at
//
//		http://www.apache.org/licenses/LICENSE-2.0
//
//	Unless required by applicable law or agreed to in writing, software
//	distributed under the License is distributed on an "AS IS" BASIS,
//	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//	See the License for the specific language governing permissions and
//	limitations under the License.
///////////////////////////////////////////////////////////////////////////////

using System;
using System.Collections;
using System.Text;
using System.Text.RegularExpressions;

namespace JDI.Pusher.Client
{
	internal class PusherMessage
	{
		#region Constructors

		public PusherMessage()
		{
			this.Type = PusherConst.EventType.Undefined;
			this.Name = null;
			this.Channel = null;
			this.RawData = null;
			this.Data = null;
		}

		public void Dispose()
		{
			this.Name = null;
			this.Channel = null;
			this.RawData = null;
			this.Data = null;
		}

		#endregion

		#region Properties

		public PusherConst.EventType Type { get; private set; }
		public string Name { get; private set; }
		public string Channel { get; private set; }
		public string RawData { get; private set; }
		public object Data { get; private set; }

		#endregion

		#region Static Methods

		public static PusherMessage TryParse(string message)
		{
			PusherMessage pusherMessage = null;

			string name = null;
			string channel = null;
			string data = null;
			string socketId = null;
			string userId = null;
			string userInfo = null;
			string code = null;
			string errMessage = null;

			// get event name
			Regex regex = new Regex(@"""event""\s*:\s*""(.+?)""", RegexOptions.IgnoreCase);
			Match match = regex.Match(message);
			if (match.Success == true)
				name = match.Groups[1].Value;

			// get channel name
			regex = new Regex(@"""channel""\s*:\s*""(.+?)""", RegexOptions.IgnoreCase);
			match = regex.Match(message);
			if (match.Success == true)
				channel = match.Groups[1].Value;

			// get data
			regex = new Regex(@"""data""\s*:\s*""?(\{.*\})""?(\}|,)", RegexOptions.IgnoreCase);
			match = regex.Match(message);
			if (match.Success == true)
				data = match.Groups[1].Value;

			if (name == null)
				return null;

			switch (name)
			{
				// {"event": "pusher:connection_established", "data":"{\"socket_id\":\"23086.813011\"}"}
				case PusherConst.EventName_ConnectionEstablished:
					regex = new Regex(@"\\?""socket_id\\?""\s*:\s*\\?""(.+?)\\?""", RegexOptions.IgnoreCase);
					match = regex.Match(data);
					if (match.Success == true)
					{
						socketId = match.Groups[1].Value;
					}
					pusherMessage = new PusherMessage()
					{
						Type = PusherConst.EventType.ConnectionEstablished,
						Name = name,
						Channel = channel,
						RawData = data,
						Data = new ConnectionData(socketId)
					};
					break;

				// {"event": "pusher_internal:subscription_succeeded", "channel": "presence-example-channel", "data": {"presence":{"ids": Array, "hash": Hash, "count": Integer}}}
				case PusherConst.EventName_SubscriptionSucceeded:

					// TODO - return array of memberData structs

					pusherMessage = new PusherMessage()
					{
						Type = PusherConst.EventType.SubscriptionSucceeded,
						Name = name,
						Channel = channel,
						RawData = data,
						Data = data
					};
					break;

				// {"event": "pusher_internal:subscription_error", "channel": "presence-example-channel", TBD???}
				case PusherConst.EventName_SubscriptionError:

					// TODO - not sure what will be sent in data from pusher??

					pusherMessage = new PusherMessage()
					{
						Type = PusherConst.EventType.SubscriptionError,
						Name = name,
						Channel = channel,
						RawData = data,
						Data = data
					};
					break;

				// {"event": "pusher_internal:member_added", "channel": "presence-example-channel", "data": {"user_id": String, "user_info": Object}}
				case PusherConst.EventName_MemberAdded:
					regex = new Regex(@"""user_id""\s*:\s*""(.+?)""", RegexOptions.IgnoreCase);
					match = regex.Match(data);
					if (match.Success == true)
					{
						userId = match.Groups[1].Value;
					}
					regex = new Regex(@"""user_info""\s*:\s*""(.+?)""", RegexOptions.IgnoreCase);
					match = regex.Match(data);
					if (match.Success == true)
					{
						userInfo = match.Groups[1].Value;
					}
					pusherMessage = new PusherMessage()
					{
						Type = PusherConst.EventType.MemberAdded,
						Name = name,
						Channel = channel,
						RawData = data,
						Data = new MemberData(userId, userInfo)
					};
					break;

				// {"event": "pusher_internal:member_removed", "channel": "presence-example-channel", "data": {"user_id": String,}}
				case PusherConst.EventName_MemberRemoved:
					regex = new Regex(@"""user_id""\s*:\s*""(.+?)""", RegexOptions.IgnoreCase);
					match = regex.Match(data);
					if (match.Success == true)
					{
						userId = match.Groups[1].Value;
					}
					pusherMessage = new PusherMessage()
					{
						Type = PusherConst.EventType.MemberRemoved,
						Name = name,
						Channel = channel,
						RawData = data,
						Data = userId
					};
					break;

				// {"event": "pusher:error", "data": {"message": String, "code": Integer}}
				case PusherConst.EventName_Error:
					regex = new Regex(@"""code""\s*:\s*""(.+?)""", RegexOptions.IgnoreCase);
					match = regex.Match(data);
					if (match.Success == true)
					{
						code = match.Groups[1].Value;
					}
					regex = new Regex(@"""message""\s*:\s*""(.+?)""", RegexOptions.IgnoreCase);
					match = regex.Match(data);
					if (match.Success == true)
					{
						errMessage = match.Groups[1].Value;
					}
					pusherMessage = new PusherMessage()
					{
						Type = PusherConst.EventType.Error,
						Name = name,
						Channel = channel,
						RawData = data,
						Data = new ErrorData(code, errMessage)
					};
					break;

				// {"event": String, "channel": String, "data": Hash}
				default:
					pusherMessage = new PusherMessage()
					{
						Type = PusherConst.EventType.Application,
						Name = name,
						Channel = channel,
						RawData = data,
						Data = data
					};
					break;
			}

			// cleanup
			name = null;
			channel = null;
			data = null;
			userId = null;
			userInfo = null;
			code = null;
			errMessage = null;

			return pusherMessage;
		}

		#endregion

		#region Embedded EventData Structs

		public struct ConnectionData
		{
			public ConnectionData(string socketID)
			{
				this.SocketID = socketID;
			}

			public readonly string SocketID;
		}

		public struct MemberData
		{
			public MemberData(string userId, string userInfo)
			{
				this.UserId = userId;
				this.UserInfo = userInfo;
			}

			public readonly string UserId;
			public readonly string UserInfo;
		}

		public struct ErrorData
		{
			public ErrorData(string code, string message)
			{
				this.Code = code;
				this.Message = message;
			}

			public readonly string Code;
			public readonly string Message;
		}

		#endregion
	}
}
